package com.sfh.gd.service.impl;

import cn.dev33.satoken.util.SaResult;
import cn.hutool.core.util.IdUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sfh.gd.converter.GameConverter;
import com.sfh.gd.converter.GamePKConverter;
import com.sfh.gd.converter.GameTeamConverter;
import com.sfh.gd.domain.dto.GameRuleDTO;
import com.sfh.gd.domain.dto.SysGameSignUpDTO;
import com.sfh.gd.domain.dto.web.*;
import com.sfh.gd.domain.entity.*;
import com.sfh.gd.domain.vo.GamePKVO;
import com.sfh.gd.domain.vo.GameScoreSortVO;
import com.sfh.gd.mapper.GameMapper;
import com.sfh.gd.mapper.GamePKMapper;
import com.sfh.gd.mapper.GameRegistrationMapper;
import com.sfh.gd.mapper.GameTeamMapper;
import com.sfh.gd.service.GamePKService;
import com.sfh.gd.service.GameTeamService;
import com.sfh.gd.service.rule.RuleFactory;
import com.sfh.gd.util.RedisCacheUtil;
import com.sfh.gd.util.StpKit;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * 赛事PK
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GamePKServiceImpl extends ServiceImpl<GamePKMapper, GamePKDO> implements GamePKService {

    private final GameMapper gameMapper;
    private final GameTeamService gameTeamService;

    private final RedisCacheUtil redisCacheUtil;
    private final ScheduledExecutorService scheduledExecutorService;
    private static final Map<Long, ScheduledFuture<?>> GAME_POLL_TASKS = new ConcurrentHashMap<>();

    @Override
    @Transactional
    public SaResult teamAbandon(PKAbandonTeamDTO data) {
        //PK队伍弃权
        GameDO game = gameMapper.selectOneById(data.getGameId());
        if (game == null) {
            return SaResult.error("赛事不存在");
        }
        if (game.getStatus() == 4) {
            return SaResult.error("赛事已取消,请恢复赛事状态");
        }
        GamePKDO gamePKDO = this.getMapper().selectOneById(data.getPkId());
        if (gamePKDO == null) {
            return SaResult.error("PK不存在");
        }

        RuleFactory.Compute compute = RuleFactory.getCompute(game);
        if (compute != null) {
            gamePKDO = compute.computeTeamAbandon(data.getWinTeamId(), gamePKDO);
            SaResult updateBatch = getSaResult(game, gamePKDO);
            if (updateBatch != null) return updateBatch;
        }
        return SaResult.error("操作失败，请重试提交");
    }

    @Override
    public SaResult getTeamPKList(Long teamId) {
        //获取队伍赛事所有对阵信息
        List<GamePKDO> pkdoList = this.getMapper().selectListByQuery(QueryWrapper.create().eq(GamePKDO::getTeamAId, teamId).orderBy(GamePKDO::getRoundCount, true));
        List<GamePKVO> pkvoList = GamePKConverter.INSTANCE.doToPKVO(pkdoList);
        return SaResult.data(pkvoList);
    }

    @Override
    public SaResult getGamePKList(Long gameId) {
        //获取赛事所有PK对阵详情信息
        List<GamePKDO> gamePKDOList = this.getMapper().selectListByQuery(QueryWrapper.create().eq(GamePKDO::getGameId, gameId));
        List<GamePKVO> gamePKVOList = GamePKConverter.INSTANCE.doToPKVO(gamePKDOList);
        return SaResult.data(gamePKVOList);
    }

    @Override
    public SaResult getGamePKListing(Long gameId) {
        //获取赛事所有PK对阵详情信息集合(进行中PK)
        List<GamePKVO> list = redisCacheUtil.getCacheList(PK_RUNNING_TAG + gameId);
        if (list == null || list.isEmpty()) {
            //缓存为空,查询数据库 进行中1
            List<GamePKDO> gamePKDOList = this.getMapper().selectListByQuery(QueryWrapper.create().eq(GamePKDO::getGameId, gameId).eq(GamePKDO::getState, 1));
            List<GamePKVO> gamePKVOList = GamePKConverter.INSTANCE.doToPKVO(gamePKDOList);
            if (gamePKVOList != null && !gamePKVOList.isEmpty()) {
                //缓存数据
                redisCacheUtil.setCacheList(PK_RUNNING_TAG + gameId, gamePKVOList);
            }
            return SaResult.data(gamePKVOList);
        }
        return SaResult.ok();
    }

    @Override
    public SaResult getPKInfo(Long pkId) {
        GamePKDO gamePKDO = this.getMapper().selectOneById(pkId);
        if (gamePKDO == null) {
            return SaResult.error("PK不存在");
        }
        GamePKVO gamePKVO = GamePKConverter.INSTANCE.doToPKVO(gamePKDO);
        return SaResult.data(gamePKVO);
    }

    @Override
    public SaResult addPKResult(PKResultNewDTO data) {
        //PK队伍录入结果
        GameDO game = gameMapper.selectOneById(data.getGameId());
        if (game == null) {
            return SaResult.error("赛事不存在");
        }
        if (game.getStatus() == 4) {
            return SaResult.error("赛事已取消,请恢复赛事状态");
        }
        GamePKDO gamePKDO = this.getMapper().selectOneById(data.getPkId());
        if (gamePKDO == null) {
            return SaResult.error("PK不存在");
        }

        RuleFactory.Compute compute = RuleFactory.getCompute(game);
        if (compute != null) {
            gamePKDO = compute.computeTeamNormal(data.getWinTeamId(), gamePKDO);
            gamePKDO.setWinLevel(data.getWindLevel());
            gamePKDO.setLoseLevel(data.getLoseLevel());

            SaResult updateBatch = getSaResult(game, gamePKDO);
            if (updateBatch != null) return updateBatch;
        }
        return SaResult.error("操作失败，请重试提交");
    }

    private SaResult getSaResult(GameDO game, GamePKDO gamePKDO) {
        int update = this.getMapper().update(gamePKDO);
        if (update > 0) {
            //更新队伍积分
            List<GameTeamDO> gameTeamDOList = new ArrayList<>();
            gameTeamDOList.add(gamePKDO.getTeamA());
            gameTeamDOList.add(gamePKDO.getTeamB());
            boolean updateBatch = gameTeamService.updateBatch(gameTeamDOList);
            if (updateBatch) {
                //进入redis 缓存中,等待匹配PK队伍
                if (gamePKDO.getTeamA().getState() == 4) {
                    redisCacheUtil.setCacheMapValue(PK_WAITING_TAG + game.getId(), String.valueOf(gamePKDO.getTeamAId()), gamePKDO.getTeamA());
                }
                if (gamePKDO.getTeamB().getScoreValue() == 4) {
                    redisCacheUtil.setCacheMapValue(PK_WAITING_TAG + game.getId(), String.valueOf(gamePKDO.getTeamBId()), gamePKDO.getTeamB());
                }
            }
            return SaResult.data(updateBatch);
        }
        return null;
    }

    private final static String PK_WAITING_TAG = "pk:waiting:";
    private final static String PK_RUNNING_TAG = "pk:running:";

    public SaResult startPk(Long gameId) {
        //开启定时任务每间隔10秒判断Redis 缓存是否有队伍在等待,并进行PK 配对
        GAME_POLL_TASKS.computeIfAbsent(gameId, gid -> {
            Runnable pollTask = () -> {
                try {
                    GameDO game = gameMapper.selectOneById(gid);
                    if (game == null || game.getStatus() != 2) {
                        return;
                    }
                    //比赛中
                    final String waitKey = PK_WAITING_TAG + gid;
                    //获取等待列表
                    Map<String, GameTeamDO> waiting = redisCacheUtil.getCacheMap(waitKey);
                    if (waiting == null || waiting.size() < 2) {
                        return;
                    }
                    //获取PK
                    RuleFactory.Pk pk = RuleFactory.getPk(game);
                    if (pk != null) {
                        //获取PK结果
                        List<GamePKDO> pkList = pk.pkResult(waiting);
                        if (pkList != null && !pkList.isEmpty()) {
                            //批量插入PK
                            boolean insertBatch = this.saveBatch(pkList);
                            if (insertBatch) {
                                //把组队成功的队伍从等待PK缓存中删除
                                List<GameTeamDO> gameTeamDOList = new ArrayList<>();
                                for (GamePKDO gamePKDO : pkList){
                                    redisCacheUtil.deleteCacheMapValue(PK_WAITING_TAG + game.getId(), String.valueOf(gamePKDO.getTeamAId()));
                                    redisCacheUtil.deleteCacheMapValue(PK_WAITING_TAG + game.getId(), String.valueOf(gamePKDO.getTeamBId()));

                                    gameTeamDOList.add(gamePKDO.getTeamA());
                                    gameTeamDOList.add(gamePKDO.getTeamB());
                                }
                                //更新队伍PK状态比赛中
                                gameTeamService.updateBatch(gameTeamDOList);
                            }
                        }
                        if (waiting.size() > 0) {
                            //未匹配失败的队伍，进入下一轮PK匹配
                            for (GameTeamDO gameTeamDO : waiting.values()) {
                                //缓存数据更新
                                redisCacheUtil.setCacheMapValue(PK_WAITING_TAG + game.getId(), String.valueOf(gameTeamDO.getGameId()),gameTeamDO);
                            }
                        }
                    }

                } catch (Exception ex) {
                    log.error("PK waiting poll error", ex);
                }
            };
            return scheduledExecutorService.scheduleAtFixedRate(pollTask, 10, 10, TimeUnit.SECONDS);
        });
        return SaResult.ok();
    }

    public SaResult endPk(Long gameId) {
        //结束比赛 结束定时任务 释放Redis缓存数据
        ScheduledFuture<?> future = GAME_POLL_TASKS.remove(gameId);
        if (future != null) {
            future.cancel(true);
        }
        //清除-等待列表
        redisCacheUtil.deleteObject(PK_WAITING_TAG + gameId);
        //清除-PK 列表
        redisCacheUtil.deleteObject(PK_RUNNING_TAG+gameId);
        //PK 状态改为结束
        List<GamePKDO> runningPKList = this.getMapper().selectListByQuery(
            QueryWrapper.create()
                .eq(GamePKDO::getGameId, gameId)
                .eq(GamePKDO::getState, 1)
        );
        if (runningPKList != null && !runningPKList.isEmpty()) {
            for (GamePKDO pk : runningPKList) {
                pk.setState(2); // 2表示结束状态
            }
            this.updateBatch(runningPKList);
        }
        //队伍状态改为正常
        List<GameTeamDO> teamList = gameTeamService.list(QueryWrapper.create().eq(GameTeamDO::getGameId, gameId));
        if (teamList != null && !teamList.isEmpty()) {
            for (GameTeamDO team : teamList) {
                team.setState(1); // 1表示正常状态
            }
            gameTeamService.updateBatch(teamList);
        }
        return SaResult.ok();
    }
}